package app.views;

import app.models.Categorie;
import app.models.Reference;
import java.awt.BorderLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseListener;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.DefaultTableModel;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Ghost
 */
public class SimpleTable<T> extends JComponent {

    private List<T> elements = new ArrayList<T>();
    private Vector<String> headers = new Vector<String>();
    private Vector<String> headersAvailable = new Vector<String>();
    private JScrollPane scrollPane;
    private JTable table;
    private DefaultTableModel model;

    public SimpleTable() {
        initComponents();
        render();
    }

    public SimpleTable(List<T> els) {
        initComponents();

        for (T el : els) {
            addElement(el);
        }

        render();
    }

    /**
     * Add an element and render the table
     * @param element the element tu append
     */
    public void addElement(T element) {
        elements.add(element);
        render();
    }

    /**
     * Add many elements and render the table
     * @param elements 
     */
    public void addElements(List<T> elements) {
        for (T el : elements) {
            addElement(el);
        }
    }

    /**
     * Replace elements by those passed and render the table
     * @param elements 
     */
    public void setElements(List<T> elements) {
        this.clear();
        addElements(elements);
    }

    /**
     * Remove un elements and render the table
     * @param element 
     */
    public void removeElement(T element) {
        elements.remove(element);
        render();
    }
    
    public void updateElement(T oldEl, T newEl){
        int id = elements.indexOf(oldEl);
        if(id != -1){
            elements.set(id, newEl);
            render();
        }
    }

    /**
     * Clear elements!
     */
    public void clear() {
        elements.clear();
        render();
    }

    /**
     * Add an header
     * @param header 
     */
    public void addHeader(String header) {
        if (!headers.contains(header)) {
            headers.add(header);
        }
    }

    /**
     * Add multiples headers
     * @param headers 
     */
    public void addHeaders(Vector<String> headers) {
        for (String header : headers) {
            addHeader(header);
        }
    }

    /**
     * set headers (erase old)
     * @param headers 
     */
    public void setHeaders(Vector<String> headers) {
        this.headers.clear();
        addHeaders(headers);
    }

    /**
     * Remove an header
     * @param header 
     */
    public void removeHeader(String header) {
        headers.remove(header);
    }

    /**
     * Get the selected element or null if no selection
     * @return 
     */
    public T getSelectedElement() {
        if (table.getSelectedRowCount() == 0) {
            return null;
        }
        
        if(elements.size() <= 0){
            return null;
        }

        return elements.get(table.getSelectedRow());
    }

    /**
     * Force the selected element
     * @param element 
     */
    public void setSelectedElement(T element) {
        if (elements.contains(element)) {
            setSelectedRow(elements.indexOf(element));
        }
    }

    /**
     * Force the selected row
     * @param row 
     */
    public void setSelectedRow(int row) {
        table.getSelectionModel().setSelectionInterval(row, row);
    }

    public List<T> getElements() {
        return (elements != null) ? elements : new ArrayList<T>();
    }

    @Override
    public synchronized void addMouseListener(MouseListener l) {
        super.addMouseListener(l);
        table.addMouseListener(l);
    }

    @Override
    public synchronized void removeMouseListener(MouseListener l) {
        table.removeMouseListener(l);
        super.removeMouseListener(l);
    }


    public int rowAtPoint(Point point) {
        return table.rowAtPoint(point);
    }

    /**
     * Get headers and elements and computes them
     */
    public void render() {
        if (headers.isEmpty()) {
            if (headersAvailable.isEmpty()) {
                headersAvailable = extractHeaders();
            }

            headers = new Vector<String>(headersAvailable); //copy!
        }
        DefaultTableModel newModel = new DefaultTableModel() {

            @Override
            public boolean isCellEditable(int row, int column) {
                return false;
            }
        };
        newModel.setRowCount(0);
        newModel.setColumnIdentifiers(headers);

        if(elements.size() > 0){
            //for each elements
            for (T element : elements) {
                Vector<String> line = new Vector<String>();
                //foreach columns
                for (String header : headers) {
                    //if this headers contains a "." that means that column
                    // contains a submethod from a subObject
                    if(header.contains(".")){
                        //so we extract all sub method wee need to call
                        String[] headerParts = header.split("\\.");
                        //manage a big try catch (equivalent of a prayer)
                        try{
                            //getting the first sub object
                            // getting the method to get the sub oject
                            Method m = element.getClass().getDeclaredMethod("get" + headerParts[0]);
                            //calling it and storing it in an Object!
                            Object obj = m.invoke(element);
                            //for each sub ojects
                            for(int i = 1; i < headerParts.length; i++){
                                //get the method
                                // (pray to find it...
                                m = obj.getClass().getDeclaredMethod("get" + headerParts[i]);
                                //call and store to our object
                                obj = m.invoke(obj);
                            }//Repeat till we get our last sub object
                            line.add(String.valueOf(obj));
                            //Ouf!
                        } catch(Exception ex){
                            throw new RuntimeException(ex);
                        }
                    } else {
                        try {
                            Method m = element.getClass().getDeclaredMethod("get" + header);
                            Object obj = m.invoke(element);
                            line.add(String.valueOf(obj));
                        } catch (Exception ex) {
                            throw new RuntimeException(ex);
                        }
                    }
                }
                newModel.addRow(line);
            }
        } else {
            Vector<String> line = new Vector<String>();
            line.add("Rien trouvé");
            if(headers.size() > 1)
                line.add("Ooooops...");
            
            newModel.addRow(line);
        }

        table.setModel(newModel);
        model = newModel;
        repaint();
    }

    private void initComponents() {
        //inits        
        headersAvailable = extractHeaders();

        scrollPane = new JScrollPane();
        model = new DefaultTableModel(
                new Object[][]{
                    {null, null, null, null}
                },
                new String[]{
                    "Col1", "Col2", "Col3", "Col4"
                });
        table = new JTable(model);

        //inserts
        scrollPane.setViewportView(table);
        table.setFillsViewportHeight(true);
        //Single selection
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        table.setCellSelectionEnabled(false);
        table.setRowSelectionAllowed(true);
        table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
        this.setLayout(new BorderLayout());
        this.add(scrollPane, BorderLayout.CENTER);
        
        table.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
    }

    private Vector<String> extractHeaders() {
        Vector<String> extractedHeaders = new Vector<String>();
        if (elements.isEmpty()) {
            return extractedHeaders;
        }
        Class c = elements.get(0).getClass();
        Method[] methods = c.getDeclaredMethods();
        //foreach getter
        for (Method m : methods) {
            String name = m.getName();
            if (name.startsWith("get")) {
                extractedHeaders.add(name.substring(3));
            }
        }

        return extractedHeaders;
    }

    public static void main(String args[]) {
        List<Reference> references = new ArrayList<Reference>();
        final Random rnd = new Random();
        int max = (int) (rnd.nextDouble() * 30) + 3;

        final Categorie cat = new Categorie("Test");

        for (int i = 1; i < max; i++) {
            Reference ref = new Reference(
                    i,
                    "Designation produit " + i,
                    rnd.nextDouble() * 10,
                    rnd.nextDouble() * 9,
                    0,
                    rnd.nextInt() * 10,
                    true);
            ref.setCategorie(cat);
            references.add(ref);
        }

        final JFrame frame = new JFrame("Test SimpleTable");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final SimpleTable<Reference> table = new SimpleTable<Reference>(references);
        //final SimpleTable<Reference> table = new SimpleTable<Reference>();
        frame.getContentPane().setLayout(new BorderLayout());
        frame.getContentPane().add(table, BorderLayout.CENTER);
        frame.setSize(500, 300);
        frame.setLocationRelativeTo(null);

        JButton btn = new JButton("Magic!");
        btn.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                Vector<String> headers = new Vector<String>(3);
                headers.add("Id");
                headers.add("Designation");
                headers.add("Stock");

                Reference ref = new Reference(
                        0,
                        "Designation produit ajouté a la volée ",
                        rnd.nextDouble() * 10,
                        rnd.nextDouble() * 9,
                        0,
                        rnd.nextInt() * 10,
                        true);
                ref.setCategorie(cat);

                try {
                    table.setHeaders(headers);

                    table.addElement(ref);
                    table.render();
                } catch (Exception ex) {
                    JOptionPane.showMessageDialog(null, ex.getMessage(),
                            "Woh putain!", JOptionPane.ERROR_MESSAGE);
                }

            }
        });
        frame.add(btn, BorderLayout.SOUTH);
        frame.setVisible(true);
    }
}